#pragma once

#include "base/allocation.h"
#include "utility/comparer.h"

#include <stdlib.h>

namespace l8
{

// vector template
template <typename T>
class Vector
{
public:
    Vector()
            : start_(NULL), length_(0)
    {}

    Vector(T* data, int length)
            : start_(data), length_(length)
    {
        ASSERT(length == 0 || (length > 0 && data != NULL));
    }

    static Vector<T> New(int length)
    {
        return Vector<T>(NewArray<T>(length), length);
    }

    // Returns a vector using the same backing storage as this one,
    // spanning from and including 'from', to but not including 'to'.
    Vector<T> SubVector(int from, int to)
    {
        ASSERT(from < length_);
        ASSERT(to <= length_);
        ASSERT(from < to);
        return Vector<T>(start() + from, to - from);
    }

    // Returns the length of the vector.
    int length() const
    { return length_; }

    // Returns whether or not the vector is empty.
    bool is_empty() const
    { return length_ == 0; }

    // Returns the pointer to the start of the data in the vector.
    T* start() const
    { return start_; }

    // Access individual vector elements - checks bounds in debug mode.
    T& operator[](int index) const
    {
        ASSERT(0 <= index && index < length_);
        return start_[index];
    }

    T& first() { return start_[0]; }

    T& last() { return start_[length_ - 1]; }

    // Returns a clone of this vector with a new backing store.
    Vector<T> Clone() const
    {
        T* result = NewArray<T>(length_);
        for (int i = 0; i < length_; i++)
        {
            result[i] = start_[i];
        }

        return Vector<T>(result, length_);
    }

    void Sort(int (*cmp)(const T*, const T*))
    {
        typedef int (*RawComparer)(const void*, const void*);
        qsort(start(), length(), sizeof(T), reinterpret_cast<RawComparer>(cmp));
    }

    void Sort()
    {
        Sort(PointerValueCompare<T>);
    }

    void Truncate(int length)
    {
        ASSERT(length <= length_);
        length_ = length;
    }

    // Releases the array underlying this vector. Once disposed the
    // vector is empty.
    void Dispose()
    {
        if (is_empty())
        {
            return;
        }

        DeleteArray(start_);
        start_ = NULL;
        length_ = 0;
    }

    inline Vector<T> operator+(int offset)
    {
        ASSERT(offset < length_);
        return Vector<T>(start_ + offset, length_ - offset);
    }

    // Factory method for creating empty vectors.
    static Vector<T> empty()
    { return Vector<T>(NULL, 0); }

protected:
    void set_start(T* start)
    { start_ = start; }

private:
    T* start_;
    int length_;
};



template <typename T, int kSize>
        class EmbeddedVector : public Vector<T>
{
public:
    EmbeddedVector()
            : Vector<T>(buffer_, kSize)
    {}

    // When copying, make underlying Vector to reference our buffer.
    EmbeddedVector(const EmbeddedVector& rhs)
            : Vector<T>(rhs)
    {
        memcpy(buffer_, rhs.buffer_, sizeof(T) * kSize);
        set_start(buffer_);
    }

    EmbeddedVector& operator=(const EmbeddedVector& rhs)
    {
        if (this == &rhs)
        {
            return *this;
        }

        Vector<T>::operator=(rhs);
        memcpy(buffer_, rhs.buffer_, sizeof(T) * kSize);
        set_start(buffer_);
        return *this;
    }

private:
    T buffer_[kSize];
};

}

